<?PHP
/**
 * OpenStation
 *
 * @Application : OpenTPL_Method.class.php
 * @Encode : 65001 Unicode ( UTF-8 NO BOM )
 * @Description : OpenTPL内置标签方法
 * @Author : HK <hkyyang@gmail.com>
 */

class OpenTPL_Method extends OpenTPL_Parse {

    // 模板方法标签库名称
    protected $tagLibName = 'Open';

    /*
     * ----------------------------------------------[ 自定义模板标签方法说明 ]----------------------------------------------
     *
     * required 必要参数, 多个以","分隔
     * optional 可选参数, 多个以","分隔
     * condition 条件表达式, 会解析标签的内容为参数(字符串)
     * close 需要闭合,是一个成对的标签,会无限级解析子嵌套标签
     * aslias 标签别名, 多个以","分隔
     * bind 方法绑定 [ 特殊标签方法使用]
     * balance 平衡标签 [ 忽略 close 属性, 强制无参数, 但会解析标签里的内容 ], 例如{* 注释 *}
     * multi-line 标签可以在多行 [ 忽略 close 属性, 强制单标签 ]
     * parent 父标签名,只能在上级(N级)标签的闭合块中被解析[ 父级不是闭合标签将毫无意义 ]
     *
     */

    /* 内置标签方法属性 */

    // 从上到下按优先顺序，一般标签不需要考虑优先顺序，特殊标签除外，如literal
    // 标签名( 方法名) => 方法属性
    protected $OpenMethods = array(
        'literal' => array( 'close' => TRUE ),
        'include' => array( 'required' => 'file' ),
        'import' => array( 'required' => 'file', 'optional' => 'nocache' ),
        'php' => array( 'close' => TRUE ),
        '*' => array( 'balance' => TRUE, 'bind' => 'comment', 'multi-line' => TRUE ),
        'section' => array( 'required' => 'loop,name', 'optional' => 'start,step,end', 'close' => TRUE ),
        'for' => array( 'required' => 'name,start,end', 'optional' => 'step', 'close' => TRUE ),
        'page' => array( 'required' => 'name,page,total', 'optional' => 'limit', 'close' => TRUE ),
        'foreach' => array( 'required' => 'loop,value', 'optional' => 'key', 'close' => TRUE ),
        'continue' => array( 'parent' => 'section,for,foreach' ),
        'if' => array( 'condition' => TRUE, 'close' => TRUE ),
        'else' => array( 'parent' => 'if' ),
        'elseif' => array( 'condition' => TRUE, 'parent' => 'if' ),
        'switch' => array( 'required' => 'name', 'close' => TRUE ),
        'case' => array( 'required' => 'value', 'close' => TRUE, 'parent' => 'switch' ),
        'break' => array( 'parent' => 'section,for,foreach,switch,case' ),
        'default' => array( 'parent' => 'switch' ),
        'strip' => array( 'close' => TRUE ),
        'ldelim' => array(),
        'rdelim' => array(),
        'javascript' => array( 'close' => TRUE ),
        'link' => array( 'optional' => 'query,app,ssl,path' ),
        'assign' => array( 'required' => 'name,value' ),

        // 继续扩展...

    );

    /**
     * Open_literal，不解析的文本内容
     *
     * @access protected
     * @param String : $content
     * @return String
     */
    protected function Open_literal( $content ) {
        $num = count( $this -> literalData );
        $this -> literalData[ $num ] = $content;
        return str_replace( '%NUM%', $num, self :: LITERAL_REPLACE_TAG );
    }

    /**
     * Open_comment，模板注释
     *
     * @access protected
     * @param String : $content
     * @return String
     */
    protected function Open_comment( $content ) {
        return '<!--' . $content . '-->';
    }

    /**
     * Open_php，PHP代码
     *
     * @access protected
     * @param String : $content
     * @return String
     */
    protected function Open_php( $content ) {
        return $this -> applyPHP( $content );
    }

    /**
     * Open_section，section循环
     *
     * @access protected
     * @param Array : $attr
     * @param String : $content
     * @return String
     */
    protected function Open_section( $attr, $content ) {
        // 解析完成的缓存
        static $section_cache = array();
        // 唯一字符串
        $cname = unique_string( $attr . $content );
        // 如果已经解析直接返回
        if( isset( $section_cache[ $cname ] ) ) {
            return $section_cache[ $cname ];
        }
        // 必要参数
        $loop = $attr[ 'loop' ];
        $name = $attr[ 'name' ];
        // 可选参数
        $start = isset( $attr[ 'start' ] ) ? intval( $attr[ 'start' ] ) : 0;
        $end = isset( $attr[ 'end' ] ) ? intval( $attr[ 'end' ] ) : 'count( ' . $loop . ' )';
        $step = isset( $attr[ 'step' ] ) ? ' += ' . intval( $attr[ 'step' ] ) : ' ++';
        // 生成可执行代码
        $code = $this -> applyPHP( 'if( is_array( ' . $loop . ' ) ): for( ' . $name . ' = ' . $start . '; ' . $name . ' < ' . $end . '; ' . $name . $step . ' ):' );
        // 解析块内容
        $block = array();
        if( preg_match( '/(.*)' . $this -> applyDelimiter( 'sectionelse' ) .  '(.*)/is', $content, $block ) ) {
            $content = $block[ 1 ];
            $sectionElse = $this -> parseMain( $block[ 2 ], 'section' );
        }
        $code .= $this -> parseMain( $content, 'section' );
        // 特殊方法内置标签SectionElse
        if( isset( $sectionElse ) ) {
            $code .= $this -> applyPHP( 'endfor; if( ! count( ' . $loop . ' ) ): ' ) . $sectionElse . $this -> applyPHP( 'endif; endif;' );
        } else {
            $code .= $this -> applyPHP( 'endfor; endif;' );
        }

        // 缓存解析内容
        $section_cache[ $cname ] = $code;

        return $code;

    }

    /**
     * Open_for，for循环
     *
     * @access protected
     * @param Array : $attr
     * @param String : $content
     * @return String
     */
    protected function Open_for( $attr, $content ) {
        // 解析完成的缓存
        static $for_cache = array();
        // 唯一字符串
        $cname = unique_string( $attr . $content );
        // 如果已经解析直接返回
        if( isset( $for_cache[ $cname ] ) ) {
            return $for_cache[ $cname ];
        }
        // 必要参数
        $name = $attr[ 'name' ];
        $start = $attr[ 'start' ];
        $end = $attr[ 'end' ];
        // 可选参数
        $step = isset( $attr[ 'step' ] ) ? '+=' . $attr[ 'step' ] : '++';
        // 生成可执行代码
        $code = $this -> applyPHP( 'for( ' . $name . ' = ' . $start . '; ' . $name . ' <= ' . $end . '; ' . $name . $step . ' ):' );
        // 解析块内容
        $code .= $this -> parseMain( $content, 'for' );
        $code .= $this -> applyPHP( 'endfor;' );
        // 缓存解析内容
        $for_cache[ $cname ] = $code;

        return $code;
    }

    /**
     * Open_page，page分页循环
     *
     * @access protected
     * @param Array : $attr
     * @param String : $content
     * @return String
     */
    protected function Open_page( $attr, $content ) {
        // 解析完成的缓存
        static $page_cache = array();
        // 唯一字符串
        $cname = unique_string( $attr . $content );
        // 如果已经解析直接返回
        if( isset( $page_cache[ $cname ] ) ) {
            return $page_cache[ $cname ];
        }
        // 当前页数
        $page = $attr[ 'page' ];
        // 总页数
        $total = $attr[ 'total' ];
        // 增长变量
        $name = $attr[ 'name' ];
        // 可选参数limit, 只循环次数, 自动以当前页平衡( 应该是一个奇数才会得到最好的结果 )
        $limit = isset( $attr[ 'limit' ] ) ? intval( $attr[ 'limit' ] ) : 0;

        // 生成执行代码
        $code = 'for( ' . $name . ' = 1; ' . $name . ' <= ' . $total . '; ' . $name . ' ++ ):' . "\r\n";
        $code .= 'if( ' . $limit . ' ): if( ' . $name . ' < ' . $page . ' - floor( ' . $limit . ' / 2 ) && ' . $total . ' - ' . $limit . ' >= ' . $name . ' ): continue; endif; ';
        $code .= 'if( ' . $name . ' > ' . $page . ' + floor( ' . $limit . ' / 2 ) && ' . $name . ' > ' . $limit . ' ): break; endif; endif;' . "\r\n";
        $code = $this -> applyPHP( $code );

        // 解析块中的内容
        $block = array();
        if( preg_match( '/(.*)' . $this -> applyDelimiter( 'nowpage' ) .  '(.*)/is', $content, $block ) ) {
            $content = $block[ 1 ];
            $nowpage = $this -> applyPHP( 'if( ' . $name . ' == ' . $page . ' ):' ) . $this -> parseMain( $block[ 2 ], 'page' );
            $nowpage .= $this -> applyPHP( 'else:' );
        }

        // 特殊标签nowpage
        if( isset( $nowpage ) ) {
            $code .= $nowpage . $this -> parseMain( $content, 'page' ) . $this -> applyPHP( 'endif; endfor;' );
        } else {
            $code .= $this -> parseMain( $content, 'page' );
            $code .= $this -> applyPHP( 'endfor;' );
        }
        // 缓存解析内容
        $page_cache[ $cname ] = $code;

        return $code;
    }


    /**
     * Open_foreach，foreach循环
     *
     * @cache
     * @access protected
     * @param Array : $attr
     * @param String : $content
     * @return String
     */
    protected function Open_foreach( $attr, $content ) {
        // 解析完成的缓存
        static $foreach_cache = array();
        // 唯一字符串
        $cname = unique_string( $attr . $content );
        // 如果已经解析直接返回
        if( isset( $foreach_cache[ $cname ] ) ) {
            return $foreach_cache[ $cname ];
        }
        // 必要参数
        $loop = $attr[ 'loop' ];
        $value = $attr[ 'value' ];
        // 可行参数
        $key = isset( $attr[ 'key' ] ) ? $attr[ 'key' ] : NULL;
        // 生成可执行代码
        if( ! empty( $key ) ) {
            $code = $this -> applyPHP( 'if( is_array( ' . $loop . ' ) ): foreach( ' . $loop . ' as ' . $key . ' => ' . $value . ' ):' );
        } else {
            $code = $this -> applyPHP( 'if( is_array( ' . $loop . ' ) ): foreach( ' . $loop . ' as ' . $value . ' ):' );
        }
        // 解析块内容
        $block = array();
        if( preg_match( '/(.*)' . $this -> applyDelimiter( 'foreachelse' ) .  '(.*)/is', $content, $block ) ) {
            $content = $block[ 1 ];
            $foreachElse = $this -> parseMain( $block[ 2 ], 'foreach' );
        }
        $code .= $this -> parseMain( $content, 'foreach' );
        // 特殊方法内置标签ForeachElse
        if( isset( $foreachElse ) ) {
            $code .= $this -> applyPHP( 'endforeach; if( ! count( ' . $loop . ' ) ): ' ) . $foreachElse . $this -> applyPHP( 'endif; endif;' );
        } else {
            $code .= $this -> applyPHP( 'endforeach; endif;' );
        }

        // 缓存解析内容
        $foreach_cache[ $cname ] = $code;

        return $code;
    }

    /**
     * Open_if，条件表达式
     *
     * @access protected
     * @param String : $condition
     * @param String : $content
     * @return String
     */
    protected function Open_if( $condition, $content ) {
        $code = $this -> applyPHP( 'if( ' . $condition . ' ):' );
        $code .= $this -> parseMain( $content, 'if' );
        $code .= $this -> applyPHP( 'endif;' );
        return $code;
    }

    /**
     * Open_else，条件表达式
     *
     * @parent if
     * @access protected
     * @param $
     * @return String
     */
    protected function Open_else() {
        return $this -> applyPHP( 'else:' );
    }

    /**
     * Open_elseif，条件表达式
     *
     * @parent if
     * @access protected
     * @param String : $condition
     * @return String
     */
    protected function Open_elseif( $condition ) {
        return $this -> applyPHP( 'elseif( ' . $condition . ' ): ' );
    }

    /**
     * Open_switch，条件表达式
     *
     * @access protected
     * @param Array : $attr
     * @param String : $content
     * @return String
     */
    protected function Open_switch( $attr, $content ) {
        $name = $attr[ 'name' ];
        if( substr( $name, 0, 1 ) == '$' ) {
            // 变量
            $name = $this -> parseInternalVar( $name );
        } else {
            // 字符串
            $name = '\'' . string_double_remove( $name, array( '"', '\'' ) ) . '\'';
        }
        $code = $this -> applyPHP( 'switch( ' . $name . ' ):' ) . $this -> parseMain( $content, 'switch' ) . $this -> applyPHP( 'endswitch;' );
        return $code;
    }

    /**
     * Open_case，条件表达式
     *
     * @parent switch
     * @access protected
     * @param String : $attr
     * @param String : $content
     * @return String
     */
    protected function Open_case( $attr, $content ) {
        $value = $attr[ 'value' ];
        if( substr( $value, 0, 1 ) == '$' ) {
            // 变量
            $value = $this -> parseInternalVar( $value );
        } else {
            // 字符串
            $value = '\'' . string_double_remove( $value, array( '"', '\'' ) ) . '\'';
        }
        $code = $this -> applyPHP( 'case ' . $value . ' : ' );
        $code .= $this -> parseMain( $content, 'case' );
        return $code;
    }

    /**
     * Open_break，条件表达式
     *
     * @parent switch | section | for | foreach
     * @access protected
     * @param $
     * @return String
     */
    protected function Open_break() {
        return $this -> applyPHP( 'break;' );
    }

    /**
     * Open_continue，条件表达式
     *
     * @parent section | for | foreach
     * @access protected
     * @param $
     * @return String
     */
    protected function Open_continue() {
        return $this -> applyPHP( 'continue;' );
    }

    /**
     * Open_default，条件表达式
     *
     * @parent switch
     * @access protected
     * @param $
     * @return String
     */
    protected function Open_default() {
        return $this -> applyPHP( 'default: ' );
    }

    /**
     * Open_strip，去除连续空白
     *
     * @access protected
     * @param String : $content
     * @return String
     */
    protected function Open_strip( $content ) {
        return preg_replace( '/>\s+</', '><', trim( $content ) );
    }

    /**
     * Open_include，载入模板文件
     * 默认根目录为当前正在解析模板的目录，按需要也可以是一个绝对路径(本地)
     *
     * @access protected
     * @param String : $attr
     * @return String
     */
    protected function Open_include( $attr ) {
        $file = $attr[ 'file' ];
        // 验证并更新载入模板缓存
        $this -> openTPL -> multiFetch( $file );
        $tplc = $this -> openTPL -> getCompileFile( $file );
        return $this -> applyPHP( 'if( $this -> tpl -> multiFetch( \'' . $file . '\' ) ): include( \'' . $tplc . '\' ); endif;' );
    }

    /**
     * Open_import，载入css、js文件
     *
     * @access protected
     * @param String : $attr
     * @return String
     */
    protected function Open_import( $attr ) {
        $file = $attr[ 'file' ];
        $noCache = isset( $attr[ 'nocache' ] ) ? '?' . $this -> applyPHP( 'echo time();' ) : '';
        $urlRoot = Cache :: get( 'HTTP.ROOT' ) . 'Static/';
        $html = '';
        switch( strtolower( get_file_ext( $file ) ) ) {
            case 'js' :
                $href = $urlRoot . 'Scripts/' . $file . $noCache;
                $html = '<script type="text/javascript" src="' . $href . '"></script>';
                break;
            case 'css' :
                $href = $urlRoot . 'Styles/' . $file . $noCache;
                $html = '<link type="text/css" rel="stylesheet" href="' . $href . '" media="all" />';
                break;
        }
        return $html;
    }

    /**
     * Open_ldelim，模板定界符左
     *
     * @access protected
     * @param $
     * @return String
     */
    protected function Open_ldelim() {
        return '__LDELIM__';
    }

    /**
     * Open_rdelim，模板定界符右
     *
     * @access protected
     * @param $
     * @return String
     */
    protected function Open_rdelim() {
        return '__RDELIM__';
    }

    /**
     * Open_javascript，脚本标签
     *
     * @access protected
     * @param String : $content
     * @return String
     */
    protected function Open_javascript( $content ) {
        return '<script type="text/javascript">' . $this -> parseMain( $content ) . "\n</script>";
    }

    /**
     * Open_link，根据URL模式组装URL
     *
     * @access protected
     * @param Array : $attr
     * @return String
     */
    protected function Open_link( $attr ) {

        if( IS_CLI && is_null( Cache :: get( 'HTTP.ROOT' ) ) ) {
            exit( 'Undefined variable HTTP.ROOT' );
        }

        $query = explode( '/', $attr[ 'query' ] );
        $path = isset( $attr[ 'path' ] ) ? $attr[ 'path' ] : '';

        if( ! empty( $path ) ) {
            $path = dirname( Cache :: get( 'HTTP.ROOT' ) ) . string_prepend_replace( string_append_replace( $path, '/' ), '/' );
        } else {
            $path = Cache :: get( 'HTTP.ROOT' );
        }

        if( isset( $attr[ 'app' ] ) ) {
            $app = string_append_replace( $attr[ 'app' ], '.php' );
        } else {
            // 以模板目录确定默认
            $app = basename( $this -> openTPL -> templatePath ) . '.php';
        }

        $url = $path . $app;

        foreach( $query as $k => $v ) {
            if( substr( $v, 0, 1 ) == '$' ) {
                // 变量|函数
                $query[ $k ] = $this -> parseVariableTag( $v );
            } else {
                // 字符串
                $query[ $k ] = $this -> parseInternalVar( '"' . string_double_remove( $v, array( '"', '\'' ) ) . '"' );
            }
        }

        if( isset( $attr[ 'ssl' ] ) && substr( $url, 0, 5 ) == 'http:' ) {
            $url = 'https:' . substr( $url, 5 );
        }

        return $this -> applyPHP( '$this -> tpl -> tplEchoURI( \'' . $url . '\', ' . implode( ', ', $query ) . ' ); ' );
    }

    /**
     * Open_assign，模板变量动态赋值
     *
     * @access protected
     * @param Array : $attr
     * @return void
     */
    protected function Open_assign( $attr ) {
        $name = $this -> parseInternalVar( $attr[ 'name' ] );
        $value = $attr[ 'value' ];
        if( substr( $value[ 0 ], 0, 1 ) == '$' ) {
            $value = $this -> parseInternalVar( $value );
        } else {
            $value = '\'' . string_double_remove( $value, array( '"', '\'' ) ) . '\'';
        }
        return $this -> applyPHP( $name . ' = ' . $value . ';' );
    }

}
?>